#include "opvalue.h"

namespace libxcom {

OpValue::OpValue() : type(TYPE_NONE)
{
    data.s64 = 0;
}

OpValue::OpValue(const OpValue &value)
{
    *this = value;
}

OpValue OpValue::Null()
{
    return OpValue();
}

OpValue &OpValue::operator=(const OpValue &value)
{
    type = value.type;
    copydata(value);
    return *this;
}

bool OpValue::IsEmpty() const
{
    return ((type == TYPE_NONE) || (type == TYPE_CMD && data.cmd.IsEmpty()) || (type == TYPE_STR && data.str == "") ||
        (type == TYPE_POINTER && data.pointer == nullptr));
}

bool OpValue::operator!=(const OpValue &value)
{
    return !operator==(value);
}

bool OpValue::operator!=(const TYPE_DEFINE type) const
{
    return !operator==(OpValue(type));
}

bool OpValue::operator==(const OpValue &value) const
{
    if (type == TYPE_NONE || value.type == TYPE_NONE) {
        if (type != TYPE_NONE || value.type != TYPE_NONE)
            return false;
        return true;
    } else if (IsInteger() && value.IsInteger())
        return Integer() == value.Integer();
    else if (IsFloat() && value.IsFloat())
        return (Float() - value.Float() > -0.000001) && (Float() - value.Float() < 0.000001);
    else if (type == TYPE_CMD && value.type == TYPE_CMD)
        return data.cmd == value.data.cmd;
    else if (type == TYPE_POINTER)
        return data.pointer == value.data.pointer;
    else if (type == TYPE_STR && value.type == TYPE_STR) {
        return data.str == value.data.str;
    } else {
        return false;
    }
}

OpValue::OpValue(int8_t v)
{
    data.s8 = v;
    type = TYPE_S8;
}

OpValue::OpValue(uint8_t v) : type(TYPE_U8)
{
    data.u8 = v;
}

OpValue::OpValue(int16_t v) : type(TYPE_S16)
{
    data.s16 = v;
}

OpValue::OpValue(uint16_t v) : type(TYPE_U16)
{
    data.u16 = v;
}

OpValue::OpValue(int32_t v) : type(TYPE_S32)
{
    data.s32 = v;
}

OpValue::OpValue(uint32_t v) : type(TYPE_U32)
{
    data.u32 = v;
}

OpValue::OpValue(int64_t v) : type(TYPE_S64)
{
    data.s64 = v;
}

OpValue::OpValue(uint64_t v) : type(TYPE_U64)
{
    data.u64 = v;
}

OpValue::OpValue(float v) : type(TYPE_F32)
{
    data.f32 = v;
}

OpValue::OpValue(double v) : type(TYPE_F64)
{
    data.f64 = v;
}

OpValue::OpValue(const char *str) : type(TYPE_STR)
{
    data.str = str;
}

OpValue::OpValue(const std::string &str) : type(TYPE_STR)
{
    data.str = str;
}

OpValue::OpValue(void *p) : type(TYPE_POINTER)
{
    data.pointer = p;
}

OpValue::OpValue(uint8_t *cmd_, int32_t len) : type(TYPE_CMD)
{
    data.cmd = CmdValue(cmd_, len);
}

OpValue::OpValue(const CmdValue &cmd_) : type(TYPE_CMD)
{
    data.cmd = cmd_;
}

/* operator=*/
OpValue &OpValue::operator=(int8_t v)
{
    release();
    type = TYPE_S8;
    data.s8 = v;
    return *this;
}

OpValue &OpValue::operator=(uint8_t v)
{
    release();
    type = TYPE_U8;
    data.u8 = v;
    return *this;
}

OpValue &OpValue::operator=(int16_t v)
{
    release();
    type = TYPE_S16;
    data.s16 = v;
    return *this;
}

OpValue &OpValue::operator=(uint16_t v)
{
    release();
    type = TYPE_U16;
    data.u16 = v;
    return *this;
}

OpValue &OpValue::operator=(int32_t v)
{
    release();
    type = TYPE_S32;
    data.s32 = v;
    return *this;
}

OpValue &OpValue::operator=(uint32_t v)
{
    release();
    type = TYPE_U32;
    data.u32 = v;
    return *this;
}

OpValue &OpValue::operator=(int64_t v)
{
    release();
    type = TYPE_S64;
    data.s64 = v;
    return *this;
}

OpValue &OpValue::operator=(uint64_t v)
{
    release();
    type = TYPE_U64;
    data.u64 = v;
    return *this;
}

OpValue &OpValue::operator=(float v)
{
    release();
    type = TYPE_F32;
    data.f32 = v;
    return *this;
}

OpValue &OpValue::operator=(double v)
{
    release();
    type = TYPE_F64;
    data.f64 = v;
    return *this;
}

OpValue &OpValue::operator=(const char *str)
{
    release();
    type = TYPE_STR;
    data.str = str;
    return *this;
}

OpValue &OpValue::operator=(const std::string &str)
{
    release();
    type = TYPE_STR;
    data.str = str;
    return *this;
}

OpValue &OpValue::operator=(void *p)
{
    release();
    type = TYPE_POINTER;
    data.pointer = p;
    return *this;
}

OpValue &OpValue::operator<<(const OpValue &value)
{
    ASSERT(type != TYPE_STR && value.type != TYPE_POINTER);

    if (type != TYPE_CMD && type != TYPE_NONE) {
        data.cmd += (uint8_t)Integer();
    }
    //        if (type != TYPE_CMD)
    //            data.cmd = new CmdValue();
    type = TYPE_CMD;
    if (value.type == TYPE_CMD)
        data.cmd += value.data.cmd;
    else if (value.type != TYPE_NONE)
        data.cmd += (uint8_t)value.Integer();

    return *this;
}

OpValue &OpValue::operator<<(const uint8_t value)
{
    ASSERT(type != TYPE_STR && type != TYPE_POINTER);

    if (type != TYPE_CMD && type != TYPE_NONE) {
        data.cmd += (uint8_t)Integer();
    }
    data.cmd += value;

    return *this;
}

OpValue OpValue::operator>>(const int32_t at)
{
    // 右移
    int32_t pos = at / 1000;
    int32_t len = at % 1000;
    OpValue opValue = OpValue(GenCmd(pos, len));

    return opValue;
}

CmdValue OpValue::GenCmd(int32_t pos_, int32_t len_) const
{
    ASSERT(type != TYPE_STR && type != TYPE_POINTER);

    if (type == TYPE_NONE)
        return CmdValue::Null();
    if (type != TYPE_CMD) {
        if (len_ == -1)
            len_ = 1;
        len_ = len_ > 8 ? 8 : len_;
        uint8_t buf[8] = {0};
        for (int i = 0; i < len_; i++) {
            buf[len_ - i - 1] = data.buf[i];
        }
        return CmdValue((uint8_t *)buf, len_);
    } else if (pos_ < 0)
        return CmdValue::Null();
    else if (pos_ == 0 && len_ == -1) {
        return data.cmd;
    } else {
        if (len_ < 0 || pos_ + len_ > data.cmd.Length())
            len_ = data.cmd.Length() - pos_;
        return CmdValue(data.cmd.CmdAt(pos_), len_);
    }
}

OpValue::~OpValue()
{
    release();
}

bool OpValue::IsString() const
{
    return type == TYPE_STR;
}

bool OpValue::IsDigital() const
{
    return ((type >= TYPE_U8 && type <= TYPE_F64) || (type == TYPE_CMD && data.cmd.Length() == 1));
}

bool OpValue::IsInteger() const
{
    return (IsDigital() && !IsFloat());
}

bool OpValue::IsFloat() const
{
    return (type == TYPE_F32 || type == TYPE_F64);
}

int64_t OpValue::Integer() const
{
    ASSERT(((type >= TYPE_U8 && type <= TYPE_F64) || (type == TYPE_CMD && data.cmd.Length() == 1)));

    switch (type) {
        case TYPE_U8:
            return data.u8;
        case TYPE_S8:
            return data.s8;
        case TYPE_U16:
            return data.u16;
        case TYPE_S16:
            return data.s16;
        case TYPE_U32:
            return data.u32;
        case TYPE_S32:
            return data.s32;
        case TYPE_U64:
            return data.u64;
        case TYPE_S64:
            return data.s64;
        case TYPE_CMD:
            return data.cmd[0];
    }

    if (IsFloat())
        return (int64_t)Float();

    return 0;
}

double OpValue::Float() const
{
    ASSERT(type != TYPE_NONE && type != TYPE_STR && type != TYPE_POINTER);

    switch (type) {
        case TYPE_F32:
            return data.f32;
        case TYPE_F64:
            return data.f64;
    }
    if (IsInteger())
        return Integer();
    return 0;
}

OpValue OpValue::calc(OP_E op, const OpValue &value1, const OpValue &value2)
{
    if (!value1.IsDigital() || !value2.IsDigital()) {
        CmdValue v1 = value1.data.cmd;
        if (value1.type != TYPE_CMD) {
            HTELINK_LOG_ERR("error value %u", value1.type);
            return OpValue::Null();
        }

        if (value2.IsDigital() || (value2.data.cmd.Length() == 1)) {
            for (int32_t i = 0; i < v1.Length(); i++) {
                switch (op) {
                    case ADD:
                        *(v1.CmdAt(i)) += value2.Integer();
                        break;
                    case SUB:
                        *(v1.CmdAt(i)) -= value2.Integer();
                        break;
                    case MUL:
                        *(v1.CmdAt(i)) *= value2.Integer();
                        break;
                    case DIV: {
                        uint8_t disor = value2.Integer();
                        if (disor != 0)
                            *(v1.CmdAt(i)) /= disor;
                        break;
                    }
                    case MOD: {
                        uint8_t disor = value2.Integer();
                        if (disor != 0)
                            *(v1.CmdAt(i)) %= disor;
                        break;
                    }
                    case XOR: {
                        uint8_t disor = value2.Integer();
                        *(v1.CmdAt(i)) ^= disor;
                        break;
                    }
                }
            }
            return v1;
        } else if (value2.type == TYPE_CMD) {
            if (v1.Length() != value2.data.cmd.Length()) {
                HTELINK_LOG_ERR("error value %u", value2.type);
                return OpValue::Null();
            }
            for (int32_t i = 0; i < v1.Length(); i++) {
                switch (op) {
                    case ADD:
                        *(v1.CmdAt(i)) += *(value2.data.cmd.CmdAt(i));
                        break;
                    case SUB:
                        *(v1.CmdAt(i)) -= *(value2.data.cmd.CmdAt(i));
                        break;
                    case MUL:
                        *(v1.CmdAt(i)) *= *(value2.data.cmd.CmdAt(i));
                        break;
                    case DIV: {
                        uint8_t disor = *(value2.data.cmd.CmdAt(i));
                        if (disor != 0)
                            *(v1.CmdAt(i)) /= disor;
                    } break;
                    case MOD: {
                        uint8_t disor = *(value2.data.cmd.CmdAt(i));
                        if (disor != 0)
                            *(v1.CmdAt(i)) %= disor;
                        break;
                    }
                    case XOR: {
                        uint8_t disor = value2.Integer();
                        *(v1.CmdAt(i)) ^= disor;
                        break;
                    }
                }
            }
            return v1;
        } else {
            HTELINK_LOG_ERR("error value %u", value2.type);
            return OpValue::Null();
        }
    }

    if (value1.IsInteger() && value2.IsInteger()) {
        switch (op) {
            case ADD:
                return value1.Integer() + value2.Integer();
            case SUB:
                return value1.Integer() - value2.Integer();
            case MUL:
                return value1.Integer() * value2.Integer();
            case DIV: {
                int64_t disor = value2.Integer();
                if (disor != 0)
                    return value1.Integer() / disor;
                return value1.Integer();
            }
            case MOD: {
                int64_t disor = value2.Integer();
                if (disor != 0)
                    return value1.Integer() % disor;
                return value1.Integer();
            }
            case XOR: {
                int64_t disor = value2.Integer();
                return value1.Integer() ^ disor;
            }
        }
    } else if (op != MOD && op != XOR) {
        double result = 0;
        if (value1.IsInteger())
            result = value1.Integer();
        else
            result = value1.Float();

        if (value2.IsInteger()) {
            switch (op) {
                case ADD:
                    result += value2.Integer();
                    break;
                case SUB:
                    result -= value2.Integer();
                    break;
                case MUL:
                    result *= value2.Integer();
                    break;
                case DIV: {
                    int32_t disor = value2.Integer();
                    if (disor != 0)
                        result /= disor;
                } break;
            }
        } else {
            switch (op) {
                case ADD:
                    result += value2.Float();
                    break;
                case SUB:
                    result -= value2.Float();
                    break;
                case MUL:
                    result *= value2.Float();
                    break;
                case DIV: {
                    if (!floatcmp(value2.Float(), 0))
                        result /= value2.Float();
                } break;
            }
        }
        return result;
    }
    return value1;
}

OpValue OpValue::operator+(const OpValue &value)
{
    OpValue result = calc(ADD, *this, value);
    if (value.IsEmpty())
        return result;
    return result;
}

OpValue OpValue::operator-(const OpValue &value)
{
    OpValue result = calc(SUB, *this, value);
    if (value.IsEmpty())
        return result;
    return result;
}

OpValue OpValue::operator*(const OpValue &value)
{
    OpValue result = calc(MUL, *this, value);
    if (value.IsEmpty())
        return result;
    return result;
}

OpValue OpValue::operator/(const OpValue &value)
{
    OpValue result = calc(DIV, *this, value);
    if (value.IsEmpty())
        return result;
    return result;
}

OpValue OpValue::operator%(const OpValue &value)
{
    OpValue result = calc(MOD, *this, value);
    if (value.IsEmpty())
        return result;
    return result;
}

OpValue OpValue::operator^(const OpValue &value)
{
    OpValue result = calc(XOR, *this, value);
    if (value.IsEmpty())
        return result;
    return result;
}

int32_t OpValue::GetByte() const
{
    switch (type) {
        case TYPE_U8:
        case TYPE_S8:
            return 1;
        case TYPE_U16:
        case TYPE_S16:
            return 2;
        case TYPE_U32:
        case TYPE_S32:
            return 4;
        case TYPE_U64:
        case TYPE_S64:
            return 8;
        case TYPE_F32:
            return 4;
        case TYPE_F64:
            return 8;
        case TYPE_CMD:
            return data.cmd.Length();
        default:
            return 0;
    }
}

void OpValue::Show()
{
    switch (type) {
        case TYPE_U8:
            HTELINK_LOG_DEBUG("%u", data.u8);
            break;
        case TYPE_S8:
            HTELINK_LOG_DEBUG("%d", data.s8);
            break;
        case TYPE_U16:
            HTELINK_LOG_DEBUG("%u", data.u16);
            break;
        case TYPE_S16:
            HTELINK_LOG_DEBUG("%d", data.s16);
            break;
        case TYPE_U32:
            HTELINK_LOG_DEBUG("%u", data.u32);
            break;
        case TYPE_S32:
            HTELINK_LOG_DEBUG("%d", data.s32);
            break;
        case TYPE_U64:
            HTELINK_LOG_DEBUG("%" PRIu64, data.u64);
            break;
        case TYPE_S64:
            HTELINK_LOG_DEBUG("%" PRId64, data.s64);
            break;
        case TYPE_F32:
            HTELINK_LOG_DEBUG("%0.3f", data.f32);
            break;
        case TYPE_F64:
            HTELINK_LOG_DEBUG("%0.3f", data.f64);
            break;
        case TYPE_CMD:
            data.cmd.Show();
            break;
        default:
            break;
    }
}

std::string OpValue::toString()
{
    char str[30];
    switch (type) {
        case TYPE_U8:
            sprintf(str, "%u", data.u8);
            break;
        case TYPE_S8:
            sprintf(str, "%d", data.s8);
            break;
        case TYPE_U16:
            sprintf(str, "%u", data.u16);
            break;
        case TYPE_S16:
            sprintf(str, "%d", data.s16);
            break;
        case TYPE_U32:
            sprintf(str, "%u", data.u32);
            break;
        case TYPE_S32:
            sprintf(str, "%d", data.s32);
            break;
        case TYPE_U64:
            sprintf(str, "%" PRIu64, data.u64);
            break;
        case TYPE_S64:
            sprintf(str, "%" PRId64, data.s64);
            break;
        case TYPE_F32:
            sprintf(str, "%0.3f", data.f32);
            break;
        case TYPE_F64:
            sprintf(str, "%0.3f", data.f64);
            break;
        case TYPE_CMD: {
            return data.cmd.toString();
        } break;
    }
    return str;
}

void OpValue::release()
{
    type = TYPE_NONE;
}

void OpValue::copydata(const OpValue &value)
{
    switch (value.type) {
        case TYPE_STR: {
            data.str = value.data.str;
            break;
        }
        case TYPE_POINTER:
            data.pointer = value.data.pointer;
            break;
        case TYPE_CMD:
            data.cmd = value.data.cmd;
            break;
        case TYPE_U8:
            data.u8 = value.data.u8;
            break;
        case TYPE_S8:
            data.s8 = value.data.s8;
            break;
        case TYPE_U16:
            data.u16 = value.data.u16;
            break;
        case TYPE_S16:
            data.s16 = value.data.s16;
            break;
        case TYPE_U32:
            data.u32 = value.data.u32;
            break;
        case TYPE_S32:
            data.s32 = value.data.s32;
            break;
        case TYPE_U64:
            data.u64 = value.data.u64;
            break;
        case TYPE_S64:
            data.s64 = value.data.s64;
            break;
        case TYPE_F32:
            data.f32 = value.data.f32;
            break;
        case TYPE_F64:
            data.f64 = value.data.f64;
            break;
        default:
            data.s64 = value.data.s64;
            break;
    }
}
} // namespace libxcom
